/**
 *  @file SyncLock.cpp
 */

#if defined(WIN32)
#   include <windows.h>
#elif defined(linux)
#   include <stdlib.h>
#   include <pthread.h>
#endif
#include "../common/CppMemPool.h"
#include "SyncLock.h"

/*
 * include this enum declaration if it is missing on Linux platform
 *
 */
#if defined(linux)

/*
 *  Use this custom attr enum instead of 'PTHREAD_MUTEX_RECURSIVE'
 */
enum {
   ATTR_PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP
};

#endif

namespace cppflib
{

namespace threading
{


SyncLock::SyncLock(void)
{
#if defined(WIN32)
   CRITICAL_SECTION *pCS = static_cast<CRITICAL_SECTION*>(::MRP_Calloc(1, sizeof(CRITICAL_SECTION)));
   ::InitializeCriticalSection(pCS);
   this->pSyncObj = static_cast<ptrany_t>(pCS);
#elif defined(linux)
   pthread_mutexattr_t mutexAttr;
   pthread_mutex_t * pMutex = static_cast<pthread_mutex_t*>(::MRP_Calloc(1, sizeof(pthread_mutex_t)));

   pthread_mutexattr_init(&mutexAttr);
   // make it recursive so that acquiring the same mutex more than once on the same thread won't cause deadlock
   pthread_mutexattr_settype(&mutexAttr, ATTR_PTHREAD_MUTEX_RECURSIVE); 
   pthread_mutex_init(pMutex, &mutexAttr);
   pthread_mutexattr_destroy(&mutexAttr);
   this->pSyncObj = static_cast<ptrany_t>(pMutex);
#endif
}

SyncLock::~SyncLock(void)
{
   Close();
}

/**
 *  Try to acquire lock, which will return immediately whether successful or not
 *
 *  @return true if lock is acquired, in which case 'Unlock' must be called
 */
bool SyncLock::TryLock(void)
{
#if defined(WIN32)
   return ::TryEnterCriticalSection(static_cast<LPCRITICAL_SECTION>(this->pSyncObj)) ? true : false;
#elif defined(linux)
   return (pthread_mutex_trylock(static_cast<pthread_mutex_t*>(this->pSyncObj)) == 0) ? true : false;
#endif
}

/**
 *  Acquire lock and block current thread until successful
 */
void SyncLock::Lock(void)
{
#if defined(WIN32)
   ::EnterCriticalSection(static_cast<LPCRITICAL_SECTION>(this->pSyncObj));
#elif defined(linux)
   pthread_mutex_lock(static_cast<pthread_mutex_t*>(this->pSyncObj));
#endif
}

/**
 *  Unlock after 'Lock' or 'TryLock' is successful
 */
void SyncLock::Unlock(void)
{
#if defined(WIN32)
   ::LeaveCriticalSection(static_cast<LPCRITICAL_SECTION>(this->pSyncObj));
#elif defined(linux)
   pthread_mutex_unlock(static_cast<pthread_mutex_t*>(this->pSyncObj));
#endif
}

/**
 *  Release resource allocated
 */
void SyncLock::Close(void)
{
   if (this->pSyncObj) {
#if defined(WIN32)
      ::DeleteCriticalSection(static_cast<LPCRITICAL_SECTION>(this->pSyncObj));
#elif defined(linux)
      pthread_mutex_destroy(static_cast<pthread_mutex_t*>(this->pSyncObj));
#endif
      cppmempool::Release(this->pSyncObj);
      this->pSyncObj = NULL;
   }
}

} // end of namespace threading

} // end of namespace cppflib

